﻿using FlyingEye;
using FlyingEye.DeviceClients;
using FlyingEye.HttpServices;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.Options;
using System.Net.Http;

namespace FlyingEyeClient.ApplicationServices.SignalRHubs
{
    /// <summary>
    /// 签名的令牌
    /// </summary>
    public class SignedToken
    {
        public string AccessToken { get; set; }

#pragma warning disable CS8618 // 在退出构造函数时，不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
        public SignedToken()
#pragma warning restore CS8618 // 在退出构造函数时，不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
        {
        }
    }

    public class SignalRClient
    {
        private readonly ApplicationOptions _applicationOptions;

        private readonly HttpClientService _httpClientService;

        public SignalRClient(
            IOptions<SignalRAuthenticationOptions> options,
            HttpClientService httpClientService,
            IOptions<ApplicationOptions> applicationOptions)
        {
            _options = options.Value;
            _httpClientService = httpClientService;
            _applicationOptions = applicationOptions.Value;
        }

        private HubConnection? _connection;

        private readonly SignalRAuthenticationOptions _options;

        public event EventHandler<SignalRClientClosedEventArgs>? Closed;
        public event EventHandler<SignalRClientReconnectedEventArgs>? Reconnected;

        public bool IsConnected => _connection?.State == HubConnectionState.Connected;

        /// <summary>
        /// 创建访问令牌
        /// </summary>
        private async Task<string?> CreateAccessTokenAsync(string resourceId)
        {
            var token = await _httpClientService.PostAsync<SignedToken, Object>(
                $"{_applicationOptions.HttpBaseUrl}/api/device-app-service/v1.0/signatures/add-claims", new
                {
                    Claims = new Dictionary<string, Object>()
                    {
                        {"resource_id",resourceId }
                    }
                });

            return token.AccessToken;
        }

        /// <summary>
        /// 连接 Hub 服务器
        /// </summary>
        /// <exception cref="InvalidOperationException">注册节点失败异常</exception>
        public async Task ConnectAsync(string url, string resourceId)
        {
            var connection = new HubConnectionBuilder()
            .WithUrl(url, options =>
            {
                // 如果是 win7 系统，禁用 HTTPS 验证
                if (WindowsApi.IsWindows7())
                {
                    options.HttpMessageHandlerFactory = handle => new HttpClientHandler()
                    {
                        ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) => true,
                    };
                }

                options.AccessTokenProvider = () =>
                {
                    return CreateAccessTokenAsync(resourceId);
                };
            })
            .WithAutomaticReconnect(new SignalRRetryPolicy()) // 每 10 秒重连
            .Build();

            // 连接到 HUB 中心
            await connection.StartAsync();

            _connection = connection;

            _connection.Closed -= OnClosed;
            _connection.Closed += OnClosed;

            _connection.Reconnected -= OnReconnected;
            _connection.Reconnected += OnReconnected;
        }

        private Task OnReconnected(string? arg)
        {
            Reconnected?.Invoke(this, new SignalRClientReconnectedEventArgs(arg));
            return Task.CompletedTask;
        }

        private Task OnClosed(Exception? arg)
        {
            Closed?.Invoke(this, new SignalRClientClosedEventArgs(arg));
            return Task.CompletedTask;
        }

        /// <summary>
        /// 从服务端调用客户端
        /// </summary>
        public HubConnection On(Action<HubConnection> action)
        {
            if (_connection == null || _connection.State != HubConnectionState.Connected)
            {
                throw new InvalidOperationException("未连接 SignalR 服务器！");
            }
            action(_connection);
            return _connection;
        }

        /// <summary>
        /// 从服务端调用客户端
        /// </summary>
        public HubConnection On<TResult>(string method, Func<TResult> func)
        {
            if (_connection == null || _connection.State != HubConnectionState.Connected)
            {
                throw new InvalidOperationException("未连接 SignalR 服务器！");
            }

            _connection.On<TResult>(method, func);

            return _connection;
        }

        /// <summary>
        /// 断开连接。
        /// </summary>
        public async Task DisconnectAsync()
        {
            // 关闭 SignalR 连接
            if (_connection != null)
            {
                await _connection.StopAsync();
                _connection = null;
            }
        }
    }
}
